home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 4 / ETO Development Tools 4.iso / Essentials / MacApp Documentation / MacApp.TECH$ Archives / 1988 / Oct⁄Nov 88 / Sound Advice ⁄ 11.08.88 ⁄ < prev    next >
Encoding:
Text File  |  1991-03-06  |  9.6 KB  |  271 lines  |  [TEXT/GEOL]

  1. Item    8439715                         8-Nov-88        17:39
  2.  
  3. From:   MACDTS                          Macintosh Developer Technical Supt.
  4.  
  5. To:     D1736                           Neurodata, Dev, Robert Norman
  6.  
  7. cc:     MACAPP.TECH$                    MACAPP Tech
  8.  
  9. Sub:    Sound advice
  10.  
  11. TWIMC,
  12.  
  13. There are a number of problems in the code I received from you.  I'm only
  14. teasing you when I say this, but for 9 lines of code there were many bugs.
  15. Although its not totally your fault, the Sound Manager chapter of Inside Mac V
  16. is lame.  All certified developers will recieve a replacement chapter from
  17. yours truly in the Dec. mailings.
  18.  
  19. * You're creating a sound channel immediately at the programs start up and
  20. holding on to it for the life of the application.  Normally this is considered
  21. unfriendly.  If you are actually using sound throughout the life of the program
  22. and using it at all times, then this is a valid thing to do.  But also realize
  23. that no other application will be able to produce sound while you're running.
  24. This includes _SysBeep.
  25.  
  26. * You don't need LoadResource after calling GetNamedResource.
  27.  
  28. * After calling GetNamedResource you don't have to lock it for SndPlay, but if
  29. you are using the sound continuously this might be a good idea.  But, if this
  30. sound is large you may find your getting into memory problems.  You should add
  31. a HMoveHi before the HLock.  Also, after a HLock you don't need the HNoPurge.
  32. Also, make sure your snd resources have their "purgable" bit set, or you add
  33. HPurge after the HUnlock.
  34.  
  35. * By the sound of your snd description, your creating a wave table.  Since the
  36. waveTable is broken on the Mac Plus/SE, then go ahead and play the wave table
  37. with the sampledSynth.  This waveTable need only be 512 bytes.  You should add
  38. in loop points for the sampledSynth.
  39.  
  40. * You are using a snd resource that probably specifies "which synth" and also
  41. contains additional synth information.  Every call to SndPlay will attempt to
  42. allocate more synth memory and adds that synth to the channel as a modifier.
  43. The first call to SndPlay got you into trouble.  Every call after made it even
  44. worse.
  45.  
  46. * You should install the sampled sound buffer into the channel and then send
  47. note commands instead.  I'll send you my Pascal routines that I've written to
  48. do this.
  49.  
  50.  
  51. The code below is what I use to play snd resources.  The snd must specify which
  52. synth is require to produce the sound with my AsynchSndPlay routine.  I first
  53. open a channel without linking it to a synth.  Then call SndPlay.  The global
  54. flag "gCallBackCalled" will be set to TRUE once the sound is done.  The
  55. procedure "AsynchSndPlay" can be called at any time.  It will first dispose of
  56. the channel if it is in use.  You could also keep the channel open and use the
  57. flushCmd and quietCmd with SndDoImmediate.  This is exactly what
  58. SndDisposeChannel does.  If you are only using one synthesizer, then go ahead
  59. and keep it open.  But, don't keep a sound channel open longer than you have
  60. to.  Open it, make the sound, and then dispose of it as soon as you're done.
  61. There is very little overhead in SndNewChannel and SndDisposeChannel.
  62.  
  63. The callBack procedure is necessary when playing sound asynchronously.  The
  64. main event loop can test for the global flag "gCallBackCalled" and then dispose
  65. of the channel and release any locked resources.  If you don't like this
  66. method, you can get to the sampled sound data yourself using some pointer math.
  67.  You need to locate the sampled sound buffer in the resource and then locked it
  68. into memory.  Pass the pointer to this buffer and use the bufferCmd.  This will
  69. allow you to keep the channel open and send it as many bufferCmds you'd like.
  70. You could also use the soundCmd, which will install the sample as an
  71. instrument.  Then you could play a melody with that sound using note and rest
  72. commands.
  73.  
  74. My ErrOut procedure isn't shown.  It is my personal debugging alert and is not
  75. intended for use in a real application.  You can have your own.  MacApp's is
  76. better yet.  You may want to look at Signals, which is an error recovery
  77. procedure from MacDTS.  Another important point, the main event loop will test
  78. for the global flag "gCallBackCalled" and then calls ClearChannel(gChan).  This
  79. will dispose of the channel and the snd in use.
  80.  
  81. Jim Reekes
  82. Ethics Officer
  83. Macintosh Developer Technical Support
  84. Tuesday, November 8, 1988  12:54 PM
  85.  
  86.  
  87. {------------------------------------------------------------}
  88. FUNCTION GetNamedSnd(index : INTEGER; VAR sndHdl: Handle;
  89.                         VAR sndFormat: INTEGER): OSErr;
  90. {This is a handy routine that will go fetch a snd resouce from a
  91.  name contained in a string list.  This allows us to easily
  92.  change snds without recompiling.  Also, it returns the current
  93.  snd format. Any error returned will be the resErr.}
  94.  
  95. TYPE
  96.     sndRec = RECORD
  97.         format      : INTEGER;
  98.         numSynths   : INTEGER;
  99.         ResID       : INTEGER;
  100.         END;
  101.  
  102.     sndRecPtr = ^sndRec;
  103.     sndRecHandle = ^sndRecPtr;
  104.  
  105. VAR
  106.     namedSnd    : STR255;
  107.  
  108. BEGIN
  109.     GetIndString (namedSnd, sndStrID, index);
  110.     IF namedSnd <> '' THEN
  111.         sndHdl := GetNamedResource ('snd ', namedSnd);
  112.  
  113.     IF sndHdl <> NIL THEN
  114.         sndFormat := sndRecHandle(sndHdl)^^.format;
  115.  
  116.     GetNamedSnd := ResError; {return the error}
  117. END;
  118.  
  119. {------------------------------------------------------------}
  120. Procedure DoCallBack(theChan: SndChannelPtr; theCmd: SndCommand);
  121. {This will be called at interrupt time and first gets the A5
  122.  passed to us in the callBackCmd.  It sets a global flag to be
  123.  tested in the main event loop.}
  124.  
  125. VAR
  126.     theA5   : LONGINT;
  127.  
  128. BEGIN
  129.     theA5 := SetA5(theCmd.param2); {refer to tech note 208}
  130.     gCallBackCalled := TRUE;
  131.     theA5 := SetA5(theA5);
  132. END;
  133.  
  134. {------------------------------------------------------------}
  135. Procedure InstallTheCallBack(theChan: SndChannelPtr);
  136. {This will install a callBackCmd into the channel and passes
  137.  the application's A5.}
  138.  
  139. VAR
  140.     myCmd   : sndCommand;
  141.     myErr   : OSErr;
  142.  
  143. BEGIN
  144.     WITH myCmd DO BEGIN
  145.         cmd := callBackCmd;
  146.         param1 := 0;
  147.         param2 := SetCurrentA5; {refer to tech note 208}
  148.     END;
  149.  
  150.     myErr := SndDoCommand (theChan, myCmd, FALSE);
  151.     IF myErr <> noErr THEN
  152.         ErrOut(myErr, ' after callBackCmd');
  153. END;
  154.  
  155. {------------------------------------------------------------}
  156. Procedure ClearChannel(VAR theChan: SndChannelPtr);
  157. {This will wipe out any channel and clear the chanPtr.  If the
  158.  channel is in use, it will stop any sound and then clear it.}
  159.  
  160. VAR
  161.     myErr   : OSErr;
  162.  
  163. BEGIN
  164.     IF (theChan <> NIL) THEN BEGIN
  165.         myErr := SndDisposeChannel(theChan, TRUE);
  166.         IF myErr <> noErr THEN
  167.             ErrOut(myErr, ' after SndDisposeChannel');
  168.         theChan := NIL;
  169.     END;
  170.  
  171.     IF gSndHandle <> NIL THEN BEGIN
  172.         HUnlock (gSndHandle); {we're done with the snd}
  173.         gSndHandle := NIL;
  174.     END;
  175.  
  176.     gCallBackCalled := FALSE;
  177. END;
  178.  
  179. {------------------------------------------------------------}
  180. Procedure AsynchSndPlay;
  181. {This will call SndPlay.  The snd must be either a format 2 or
  182.  format 1 that contains synth information.  We use a global
  183.  resouce handle for the snd.}
  184.  
  185. VAR
  186.     myErr       : OSErr;
  187.     sndFormat   : INTEGER;
  188.  
  189. BEGIN
  190.     ClearChannel(gChan);
  191.     myErr := GetNamedSnd(1, gSndHandle, sndFormat);
  192.     IF myErr = noErr THEN BEGIN
  193.  
  194. {create a channel without linking a synth resouce}
  195.         myErr := SndNewChannel (gChan, 0, 0, @DoCallBack);
  196.         IF myErr = noErr THEN BEGIN
  197.             myErr := SndPlay (gChan, gSndHandle, TRUE);
  198.             IF myErr <> noErr THEN
  199.                 ErrOut(myErr, ' after SndPlay');
  200.  
  201.             InstallTheCallBack(gChan);
  202.         END ELSE
  203.             ErrOut(myErr, ' after SndNewChannel');
  204.     END ELSE
  205.         ErrOut(myErr, ' after getting resource');
  206. END;
  207.  
  208. {------------------------------------------------------------}
  209. PROCEDURE PlaySndBuffer;
  210. {This routine will install a sampled sound from a format 2
  211.  resouce using the soundCmd.  This same procedure could
  212.  include a TYPE for the format 1 resource, and then support
  213.  both formats.  We use a global resource handle for the snd.
  214.  This snd will have to be locked after a moving it higher
  215.  in the heap.  Once the snd is installed, send some notes
  216.  to play a melody.}
  217.  
  218. TYPE
  219.     snd2Header = RECORD
  220.         format      : INTEGER;
  221.         refCount    : INTEGER;
  222.         numCmds     : INTEGER;
  223.         firstCmd    : SndCommand;
  224.     END;
  225.     snd2HdrPtr = ^snd2Header;
  226.  
  227. VAR
  228.     myErr       : osErr;
  229.     myCmd       : sndCommand;
  230.     firstCmdPtr : Ptr;
  231.     headerPtr   : snd2HdrPtr;
  232.     dataPtr     : SoundHeaderPtr; {from Sound.p interface file}
  233.     sizeOfCmds, pitch   : longInt;
  234.     theFormat   : INTEGER;
  235.  
  236. BEGIN
  237.     ClearChannel(gChan);
  238.     myErr := GetNamedSnd(2, gSndHandle, sndFormat);
  239.     IF (myErr = noErr) AND (sndFormat = 2) THEN BEGIN
  240.         myErr := SndNewChannel (gChan, sampledSynth, 0, @DoCallBack);
  241.  
  242.         IF myErr = noErr THEN BEGIN {pointer math to find sample}
  243.             MoveHHi(gSndHandle);
  244.             Hlock(gSndHandle);
  245.             headerPtr := snd2HdrPtr(gSndHandle^);
  246.             firstCmdPtr := @headerPtr^.firstCmd;
  247.             sizeOfCmds := headerPtr^.numCmds * SizeOf(sndCommand);
  248.             dataPtr := Pointer(ORD4(firstCmdPtr) + sizeOfCmds);
  249.  
  250.             WITH myCmd DO BEGIN
  251.                     cmd := soundCmd;
  252.                     param1 := 0;
  253.                     param2 := ORD4(DataPtr); {pointer to sample}
  254.                 END;
  255.  
  256.             myErr := SndDoCommand(gChan, myCmd, FALSE);
  257.             IF myErr <> noErr THEN
  258.                 ErrOut(myErr, 'after do sound cmd');
  259.  
  260.             PlaySomeNotes(gChan); {send some noteCmds}
  261.             InstallTheCallBack(gchan);
  262.         END ELSE
  263.             ErrOut(myErr, ' after sndNewChannel')
  264.     END ELSE
  265.         ErrOut(myErr, ' after getting resource');
  266. END;
  267.  
  268.  
  269.  
  270.  
  271.